package stcpscheculer;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class GASchedule {

    //TODO Generate Init Population
    //TODO Selection
    //TODO Crossover
    //TODO Mutation
    //TODO Termination
    private int size;
    private int Buses;
    private int Drivers;
    private int TimeSlot; // in hours
    private Population currentPopulation;
    private Population selectedPopulation;
    private Population nextPopulation;
    private int maxGenerations;
    private int numOfGenerations;
    private int chrLength;

    public GASchedule() {

        size = Configuration.getPopulationSize();
        Buses = Configuration.getBuses();
        Drivers = Configuration.getDrivers();
        TimeSlot = Configuration.getTimeSlot();
        maxGenerations = Configuration.getMaxGenerations();
        chrLength = Configuration.getChromosomeLength();
        
    }

    public Chromosome runGA(){
        Chromosome solution = new Chromosome();
        generatePopulation();
        for(int i=1;i<maxGenerations;i++){
            selection();
            crossover();
            mutation();
            if(termination()){
                // print best result
                //break;
                
                solution = currentPopulation.getBestSolution();
            }
        }
        return solution;
    }
    
    /**
     * Creates initial generation
     *
     */
    public void generatePopulation() {
        currentPopulation = new Population();
        currentPopulation.initPopulation();
        currentPopulation.evaluatePopulation();
    }

    /**
     * Selects chromosomes to produce next generation. Uses elitism
     */
    public void selection() {
        int[] selected;
        selected = new int[size - 3];
        selectedPopulation = new Population();
        addElite();
        currentPopulation.calcPsel();
        selected = currentPopulation.selectionRoulete();
        for (int i = 0; i < size - 3; i++) {
            selectedPopulation.Population[i + 3] = currentPopulation.Population[selected[i]];
        }
    }

    public void addElite() {
        // TODO select 3 best chromosomes & add to selectedPopulation
        Chromosome best1 = new Chromosome();
        Chromosome best2 = new Chromosome();
        Chromosome best3 = new Chromosome();
        float FA1 = 0;
        float FA2 = 0;
        float FA3 = 0;
        for (Chromosome chr : currentPopulation.getChromosomes()) {
            if (chr.getAdaptation() >= FA1) {
                FA3 = FA2;
                FA2 = FA1;
                FA1 = chr.getAdaptation();
                best3 = best2;
                best2 = best1;
                best1 = chr;
            }
        }
        selectedPopulation.Population[0] = best1;
        selectedPopulation.Population[1] = best2;
        selectedPopulation.Population[2] = best3;
    }

    public void crossover() {
        int[] toCross = null;
        nextPopulation = new Population();
        nextPopulation = selectedPopulation;
        toCross = nextPopulation.crossRoulete();
        if(toCross!=null){
            Random generator = new Random();
            int CrossPoint;
            for(int i=0;i<toCross.length-1;i++){
                Chromosome chr = new Chromosome();
                // choose a crosspoint randomly
                CrossPoint = generator.nextInt(chrLength);
                //cross pair i & i+1
                // copy i to chr
                chr.setSolutionArr(0, chrLength, 
                        nextPopulation.Population[i].getSolutionArr());
                // copy first part of i+1 to i
                nextPopulation.Population[i].setSolutionArr(0, CrossPoint, 
                        nextPopulation.Population[i+1].getSolutionArr());
                // copy second part of chr to i+1
                nextPopulation.Population[i+1].setSolutionArr(CrossPoint, 
                        chrLength - CrossPoint, 
                        chr.getSolutionArr());
            }
        }
    }

    public void mutation() {
        nextPopulation.mutate();
    }

    public boolean termination() {
        nextPopulation.evaluatePopulation();
        // copy new generation to current
        currentPopulation = nextPopulation;
        if (nextPopulation.bestSolution().getAdaptation() >= 
                Configuration.getDesiredFA()) { // or if good solution achieved
            return true;
        }
        return false;
    }
}
